package cloud.tianai.ram.policy.server.permission.impl;

import cloud.tianai.ram.policy.common.handler.StandardMatchHandler;
import cloud.tianai.ram.policy.common.model.dto.Effect;
import cloud.tianai.ram.policy.common.model.dto.Match;
import cloud.tianai.ram.policy.common.model.dto.Statement;
import cloud.tianai.ram.policy.common.comparator.StatementComparator;
import cloud.tianai.ram.policy.common.model.dto.Action;
import cloud.tianai.ram.policy.common.handler.PermissionHandler;
import org.springframework.core.annotation.AnnotationAwareOrderComparator;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;

/**
 * @Author: 天爱有情
 * @date 2022/3/7 14:48
 * @Description DefaultStatementHandler
 */
public class DefaultPermissionHandler implements PermissionHandler {

    private List<StandardMatchHandler> standardMatchHandlerList = new ArrayList<>(3);

    public DefaultPermissionHandler() {
    }

    public DefaultPermissionHandler(List<StandardMatchHandler> standardMatchHandlerList) {
        this.standardMatchHandlerList.addAll(standardMatchHandlerList);
        // 排序
        AnnotationAwareOrderComparator.sort(this.standardMatchHandlerList);
    }

    public List<StandardMatchHandler> getStandardMatchHandlerList() {
        return Collections.unmodifiableList(standardMatchHandlerList);
    }

    public void addStandardMatchHandler(StandardMatchHandler standardMatch) {
        removeStandardMatchHandler(standardMatch);
        this.standardMatchHandlerList.add(standardMatch);
        // 排序
        AnnotationAwareOrderComparator.sort(this.standardMatchHandlerList);
    }

    public void addStandardMatchHandler(Collection<StandardMatchHandler> standardMatchHandlerList) {
        for (StandardMatchHandler standardMatchHandler : standardMatchHandlerList) {
            removeStandardMatchHandler(standardMatchHandler);
        }
        this.standardMatchHandlerList.addAll(standardMatchHandlerList);
        // 排序
        AnnotationAwareOrderComparator.sort(this.standardMatchHandlerList);
    }

    public void removeStandardMatchHandler(StandardMatchHandler standardMatch) {
        this.standardMatchHandlerList.remove(standardMatch);
    }

    public void clearStandardMatchHandler(StandardMatchHandler standardMatch) {
        this.standardMatchHandlerList.clear();
    }


    @Override
    public Match handle(Action action, Statement statement) {
        /*
        1. 匹配资源是否通过
        2. 匹配行为是否通过
        3. 如果有条件，则匹配条件
         */
        Match result = Match.ALLOW;
        for (StandardMatchHandler standardMatchHandler : standardMatchHandlerList) {
            if (standardMatchHandler.handle(action, statement).isDeny()) {
                result = Match.IMPLICIT_DENY;
                break;
            }
        }
        Effect effect = statement.getEffect();
        return wrapMatch(result, effect);
    }

    private Match wrapMatch(Match match, Effect effect) {
        if (Effect.Deny.equals(effect) && match.isAllow()) {
            return Match.EXPLICIT_DENY;
        }
        return match;
    }

    @Override
    public Match handle(Action action, List<Statement> statements, StatementComparator statementComparator) {
        // 排序
        statementComparator.sort(statements);
        return handle(action, statements);
    }

    @Override
    public Match handle(Action action, List<Statement> statements) {
        for (Statement statement : statements) {
            Match match = handle(action, statement);
            // 如果是允许或者隐式拒绝则直接返回，否则继续匹配
            if (Match.ALLOW.equals(match) || Match.EXPLICIT_DENY.equals(match)) {
                return match;
            }
        }
        // 隐式拒绝
        return Match.IMPLICIT_DENY;
    }

    @Override
    public boolean contains(List<Statement> statements, Statement matched) {
        List<String> actionList = matched.getActionList();
        for (String action : actionList) {
            for (String resource : matched.getResourceList()) {
                if (handle(new Action(resource, action), statements).isAllow()) {
                    return true;
                }
            }
        }
        return false;
    }

}
